/******************
CMemoryPool.h
******************/

/*!\file CMemoryPool.h
* \brief Contains the "CMemoryPool" Class-defintion.
*/

#ifndef __INC_CMemoryPool_h__
#define __INC_CMemoryPool_h__

#include "IMemoryBlock.h"
#include "SMemoryChunk.h"

static const std::size_t DEFAULT_MEMORY_POOL_SIZE        = 1000 ;                          //!< Initial MemoryPool size (in Bytes)
static const std::size_t DEFAULT_MEMORY_CHUNK_SIZE       = 128 ;                           //!< Default MemoryChunkSize (in Bytes)
static const std::size_t DEFAULT_MEMORY_SIZE_TO_ALLOCATE = DEFAULT_MEMORY_CHUNK_SIZE * 2 ; //!< Default Minimal Memory-Size (in Bytes) to Allocate. 

/*!\class CMemoryPool
* \brief MemoryPool-Class
*
* This class is the actual implementation of the IMemoryBlock - Interface.
* It is responsible for all MemoryRequests (GetMemory() / FreeMemory()) and
* manages the allocation/deallocation of Memory from the Operating-System.
*/
class CMemoryPool : public IMemoryBlock
{
public :
	/*!
	Contructor
	\param [IN] sInitialMemoryPoolSize The Initial Size (in Bytes) of the Memory Pool
	\param [IN] sMemoryChunkSize The Size (in Bytes) each MemoryChunk can Manage. 
	A low sMemoryChunkSize increases the MemoryPool runtime (bad), but decreases the Memory-overhead/fragmentation (good)
	\param [IN] sMinimalMemorySizeToAllocate The Minimal amount of Memory which is allocated (in Bytes).
	That means, every time you have to allocate more Memory from the Operating-System,
	<b>at least</b> sMinimalMemorySizeToAllocate Bytes are allocated.
	When you have to request small amount of Memory very often, this will speed up
	the MemoryPool, beacause when you allocate a new Memory from the OS,
	you will allocate a small "Buffer" automatically, wich will prevent you from
	requesting OS-memory too often.
	\param [IN] bSetMemoryData Set to true, if you want to set all allocated/freed Memory to a specific
	Value. Very usefull for debugging, but has a negativ impact on the runtime.
	*/
	//CMemoryPool(const std::size_t &sInitialMemoryPoolSize = DEFAULT_MEMORY_POOL_SIZE, 
	//	const std::size_t &sMemoryChunkSize = DEFAULT_MEMORY_CHUNK_SIZE,
	//	const std::size_t &sMinimalMemorySizeToAllocate = DEFAULT_MEMORY_SIZE_TO_ALLOCATE,
	//	bool bSetMemoryData = false) ;
	CMemoryPool(std::size_t sInitialMemoryPoolSize = DEFAULT_MEMORY_POOL_SIZE, 
		std::size_t sMemoryChunkSize = DEFAULT_MEMORY_CHUNK_SIZE,
		std::size_t sMinimalMemorySizeToAllocate = DEFAULT_MEMORY_SIZE_TO_ALLOCATE,
		bool bSetMemoryData = false) ;

	virtual ~CMemoryPool() ; //!< Destructor

	/*!
	Get "sMemorySize" Bytes from the Memory Pool.
	\param [IN] sMemorySize       Sizes (in Bytes) of Memory.
	\return                       Pointer to a Memory-Block of "sMemorySize" Bytes, or NULL if an error occured. 
	*/
	virtual void *GetMemory(const std::size_t &sMemorySize) ;

	/*!
	Free the allocated memory again....
	\param [IN] ptrMemoryBlock    Pointer to a Block of Memory, which is to be freed (previoulsy allocated via "GetMemory()").
	\param [IN] sMemorySize       Sizes (in Bytes) of Memory.
	*/
	virtual void FreeMemory(void *ptrMemoryBlock, const std::size_t &sMemoryBlockSize) ;

	/*!
	Writes the contents of the MemoryPool to a File.
	Note : This file can be quite large (several MB).
	\param [IN] strFileName      FileName of the MemoryDump.
	\return                      true on success, false otherwise 
	*/
	bool WriteMemoryDumpToFile(const std::string &strFileName) ;
	/*!
	Check, if a Pointer is in the Memory-Pool.
	Note : This Checks only if a pointer is inside the Memory-Pool,
	and <b>not</b> if the Memory contains meaningfull data.
	\param [IN] ptrPointer       Pointer to a Memory-Block which is to be checked.
	\return                      true, if the Pointer could be found in the Memory-Pool, false otherwise.
	*/
	bool IsValidPointer(void *ptrPointer) ;

private :
	/*!
	Will Allocate <b>at least</b> "sMemorySize" Bytes of Memory from the Operating-System.
	The Memory will be cut into Pieces and Managed by the MemoryChunk-Linked-List.
	(See LinkChunksToData() for details).
	\param [IN] sMemorySize      The Memory-Size (in Bytes) to allocate
	\return                      true, if the Memory could be allocated, false otherwise (e.g. System is out of Memory, etc.)
	*/
	bool AllocateMemory(const std::size_t &sMemorySize) ;
	//!< Will free all Aloocated Memory to the Operating-System again.
	void FreeAllAllocatedMemory() ; 

	//!< \return the Number of MemoryChunks needed to Manage "sMemorySize" Bytes.
	unsigned int CalculateNeededChunks(const std::size_t &sMemorySize) ; 
	//!< return the amount of Memory which is best Managed by the MemoryChunks.
	std::size_t CalculateBestMemoryBlockSize(const std::size_t &sRequestedMemoryBlockSize) ; 
	//!< \return a Chunk which can hold the requested amount of memory, or NULL, if none was found.
	SMemoryChunk *FindChunkSuitableToHoldMemory(const std::size_t &sMemorySize) ; 
	//!< Find a Chunk which "Data"-Member is Pointing to the given "ptrMemoryBlock", or NULL if none was found.
	SMemoryChunk *FindChunkHoldingPointerTo(void *ptrMemoryBlock) ; 
	//!< Skip the given amount of Chunks, starting from the given ptrStartChunk. \return the Chunk at the "skipping"-Position.
	SMemoryChunk *SkipChunks(SMemoryChunk *ptrStartChunk, unsigned int uiChunksToSkip) ; 
	//!< Set "Default"-Values to the given Chunk
	SMemoryChunk *SetChunkDefaults(SMemoryChunk *ptrChunk) ; 
	//!< Makes the memory linked to the given Chunk available in the MemoryPool again (by setting the "UsedSize"-Member to 0).
	void FreeChunks(SMemoryChunk *ptrChunk) ; 
	//!< Deallocates all Memory needed by the Chunks back to the Operating-System.
	void DeallocateAllChunks() ; 
	//!< Link the given Memory-Block to the Linked-List of MemoryChunks...
	bool LinkChunksToData(SMemoryChunk *ptrNewChunk, unsigned int uiChunkCount, TByte *ptrNewMemBlock) ; 
	//!< Set the "UsedSize"-Member of the given "ptrChunk" to "sMemBlockSize".
	void SetMemoryChunkValues(SMemoryChunk *ptrChunk, const std::size_t &sMemBlockSize) ; 
	//!< Recalcs the "DataSize"-Member of all Chunks whe the Memory-Pool grows (via "AllocateMemory()")
	bool RecalcChunkMemorySize(SMemoryChunk *ptrChunks, unsigned int uiChunkCount) ; 
	//!< \return the greatest of the two input values (A or B)
	std::size_t MaxValue(const std::size_t &sValueA, const std::size_t &sValueB) const ; 
	//!< Pointer to the first Chunk in the Linked-List of Memory Chunks
	SMemoryChunk *m_ptrFirstChunk ;  
	//!< Pointer to the last Chunk in the Linked-List of Memory Chunks
	SMemoryChunk *m_ptrLastChunk ;   
	//!< Cursor-Chunk. Used to speed up the navigation in the linked-List.
	SMemoryChunk *m_ptrCursorChunk ; 
	//!< Total Memory-Pool size in Bytes
	std::size_t m_sTotalMemoryPoolSize ;  
	//!< amount of used Memory in Bytes
	std::size_t m_sUsedMemoryPoolSize ;  
	//!< amount of free Memory in Bytes
	std::size_t m_sFreeMemoryPoolSize ;   
	//!< amount of Memory which can be Managed by a single MemoryChunk.
	std::size_t m_sMemoryChunkSize ; 
	//!< Total amount of "SMemoryChunk"-Objects in the Memory-Pool.
	unsigned int m_uiMemoryChunkCount ;  
	//!< Counter for "GetMemory()" / "FreeMemory()"-Operation. Counts (indirectly) the number of "Objects" inside the mem-Pool.
	unsigned int m_uiObjectCount ;       
	//!< Set to "true", if you want to set all (de)allocated Memory to a predefined Value (via "memset()"). Usefull for debugging.
	bool m_bSetMemoryData ;    
	//!< The minimal amount of Memory which can be allocated via "AllocateMemory()".
	std::size_t m_sMinimalMemorySizeToAllocate ; 
} ;

#endif /* __INC_CMemoryPool_h__ */
